package apidoc

import (
	"autoTestNew/pkg/mongo/autoTestNew/utils"
	"context"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"net/url"
	ctrl "sigs.k8s.io/controller-runtime"
)

var log = ctrl.Log.WithName("mongodb/apiDoc")

type ApiDoc struct {
	ID *primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
	// 请求名称
	RequestName string `bson:"requestName" json:"requestName"`
	// 请求定义
	RequestDefinition RequestDefinition `bson:"requestDefinition" json:"requestDefinition"`
	// 请求描述
	RequestDescription string `bson:"requestDescription" json:"requestDescription"`
}

// Validate 检查 ApiDoc 内容是否合法，返回错误和警告
func (r *ApiDoc) Validate() (err error, warnings []string) {
	// TODO 检查请求名称
	// 检查请求定义
	err, innerWarnings := r.RequestDefinition.Validate()
	warnings = append(warnings, innerWarnings...)
	if err != nil {
		return
	}
	// TODO 检查请求描述
	return
}

type RequestDefinition struct {
	// 请求地址
	URL string `bson:"url" json:"url"`
	// 请求方法
	Method string `bson:"method" json:"method"`
	// 请求头
	Header map[string]string `bson:"header" json:"header"`
	// 请求参数
	Params map[string]Param `bson:"params" json:"params"`
	// 请求数据 TODO 支持二进制请求体
	Data string `bson:"data" json:"data"`
}

// Validate 检查 RequestDefinition 内容是否合法，返回错误和警告
func (r *RequestDefinition) Validate() (err error, warnings []string) {
	// 检查请求地址 URL 的格式
	_, err = url.Parse(r.URL)
	if err != nil {
		return
	}
	// 检查请求方法
	err, innerWarnings := utils.ValidateHTTPMethod(r.Method)
	warnings = append(warnings, innerWarnings...)
	if err != nil {
		return
	}
	// TODO 检查请求头
	// 检查请求参数
	for _, param := range r.Params {
		err, innerWarnings = param.Validate()
		warnings = append(warnings, innerWarnings...)
		if err != nil {
			return
		}
		// TODO 检查 URL 中包含请求参数
	}
	// TODO 检查请求数据
	return
}

type Param struct {
	// 参数描述
	Description string `bson:"description" json:"description"`
	// 是否必须
	Required bool `bson:"required" json:"required"`
	// 参数类型 FIXME 怎么用
	Type string `bson:"type" json:"type"`
}

// Validate 检查 Param 内容是否合法，返回错误和警告
func (p *Param) Validate() (err error, warnings []string) {
	// TODO 检查参数描述
	// TODO 检查是否必须
	// TODO 检查参数类型
	return
}

type Store struct {
	collection *mongo.Collection
}

func NewStore(db *mongo.Database) *Store {
	return &Store{
		collection: db.Collection("apiDoc"),
	}
}

// Create 创建接口文档
func (s *Store) Create(ctx context.Context, apiDoc ApiDoc) (ApiDoc, error) {
	result, err := s.collection.InsertOne(ctx, apiDoc)
	if err != nil {
		log.Error(err, "create apiDoc error", "apiDoc", apiDoc)
		return ApiDoc{}, err
	}
	objectID := result.InsertedID.(primitive.ObjectID)
	apiDoc.ID = &objectID
	return apiDoc, nil
}

// Get 获取接口文档
func (s *Store) Get(ctx context.Context, id primitive.ObjectID) (ApiDoc, error) {
	var apiDoc ApiDoc
	err := s.collection.FindOne(ctx, primitive.M{"_id": id}).Decode(&apiDoc)
	if err != nil {
		log.Error(err, "get apiDoc error", "id", id)
		return ApiDoc{}, err
	}
	return apiDoc, nil
}

// List 获取接口文档列表
func (s *Store) List(ctx context.Context) ([]ApiDoc, error) {
	var apiDocs []ApiDoc
	cursor, err := s.collection.Find(ctx, primitive.M{})
	if err != nil {
		log.Error(err, "list apiDoc error, find error")
		return nil, err
	}
	err = cursor.All(ctx, &apiDocs)
	if err != nil {
		log.Error(err, "list apiDoc error, decode error")
		return nil, err
	}
	return apiDocs, nil
}

// Update 更新接口文档
func (s *Store) Update(ctx context.Context, apiDoc ApiDoc) (ApiDoc, error) {
	_, err := s.collection.UpdateOne(ctx, primitive.M{"_id": apiDoc.ID}, primitive.M{"$set": apiDoc})
	if err != nil {
		log.Error(err, "update apiDoc error", "apiDoc", apiDoc)
		return ApiDoc{}, err
	}
	return apiDoc, nil
}

// Delete 删除接口文档
func (s *Store) Delete(ctx context.Context, id primitive.ObjectID) error {
	_, err := s.collection.DeleteOne(ctx, primitive.M{"_id": id})
	if err != nil {
		log.Error(err, "delete apiDoc error", "id", id)
		return err
	}
	return nil
}

// DeleteAll 删除所有接口文档
func (s *Store) DeleteAll(ctx context.Context) error {
	_, err := s.collection.DeleteMany(ctx, primitive.M{})
	if err != nil {
		log.Error(err, "delete all apiDoc error")
		return err
	}
	return nil
}
